package edu.cmu.cs211.bacon.tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Test;

import java.util.*;
import edu.cmu.cs211.bacon.kevin.*;


public class KevinBaconGameSolverTest {

	  public final double TOLERANCE = 0.0001;
	
    @Test(expected = NullPointerException.class)
    public void nullMapTest() throws Exception {
        KevinBaconGameSolver.createUnweightedGameSolver("Bacon, Kevin", null);
    }
    
    @Test(expected = NullPointerException.class)
    public void nullSourceTest() throws Exception {
        Map<String,List<Movie>> movieMap = createOneNodeTwoEdgeMap();
        KevinBaconGameSolver.createUnweightedGameSolver(null, movieMap);
    }

    @Test
    public void twoNodeOneEdge() throws Exception {
        Map<String,List<Movie>> movieMap = createOneNodeTwoEdgeMap();

        KevinBaconGameSolver solver = 
            KevinBaconGameSolver.createUnweightedGameSolver("Bacon, Kevin", movieMap);
        assertDoubleEquals("Two node, one edge unweighted, different dest test", 
                solver.computeDistance("Spam, Kevin"), 1.0);
        assertDoubleEquals("Two node, one edge unweighted, same dest test", 
                solver.computeDistance("Bacon, Kevin"), 0.0);

        solver = 
            KevinBaconGameSolver.createWeightedGameSolver("Bacon, Kevin", movieMap);
        assertDoubleEquals("Two node, one edge weighted different dest test", 
                solver.computeDistance("Spam, Kevin"), 18.0);          
        assertDoubleEquals("Two node, one edge weighted same dest test", 
                solver.computeDistance("Bacon, Kevin"), 0.0);
        
        solver = 
            KevinBaconGameSolver.createUnweightedGameSolver("Spam, Kevin", movieMap);
        assertDoubleEquals("Two node, one edge other source unweighted same dest test", 
                solver.computeDistance("Spam, Kevin"), 0.0);          
        assertDoubleEquals("Two node, one edge other source unweighted different dest test", 
                solver.computeDistance("Bacon, Kevin"), 1.0);

        
        solver = 
            KevinBaconGameSolver.createWeightedGameSolver("Spam, Kevin", movieMap);
        assertDoubleEquals("Two node, one edge other source weighted different dest test", 
                solver.computeDistance("Bacon, Kevin"), 18.0);          
        assertDoubleEquals("Two node, one edge other source weighted same dest test", 
                solver.computeDistance("Spam, Kevin"), 0.0);
        
    }
    
    @Test
    public void twoNodeOneEdgeMultiMovie() throws Exception {
        Map<String,List<Movie>> movieMap = createOneNodeTwoEdgeMap();
        movieMap.get("Bacon, Kevin").add(
                new Movie("Bar", 2004));
        movieMap.get("Spam, Kevin").add(
                new Movie("Bar", 2004));
        
        KevinBaconGameSolver solver = 
            KevinBaconGameSolver.createWeightedGameSolver("Bacon, Kevin", movieMap);
        assertDoubleEquals("Two node, one edge, two movie weighted different dest test", 
                solver.computeDistance("Spam, Kevin"), 7.0);          
        assertDoubleEquals("Two node, one edge, two movie weighted same dest test", 
                solver.computeDistance("Bacon, Kevin"), 0.0);          

    }    
    
    public void assertDoubleEquals(String message, double expected, double result) {
      if(Math.abs(expected - result) > TOLERANCE) {
        fail(message + ", expected: " + expected + "but was: " + result);
    }
}
    
    public Map<String,List<Movie>> createOneNodeTwoEdgeMap() {
        Map<String,List<Movie>> movieMap = new HashMap<String,List<Movie>>();
        Movie foo = new Movie("Foo", 1993);
        List<Movie> baconList = new LinkedList<Movie>();
        List<Movie> spamList = new LinkedList<Movie>();
        baconList.add(foo);
        spamList.add(foo);
        
        movieMap.put("Bacon, Kevin", baconList);
        movieMap.put("Spam, Kevin", spamList);
        return movieMap;
    }
    
    @Test
    public void testPaths()
    {
    	/*
    	 *  create movie map
    	 *  bacon: foo, bar
    	 *  spam: bar, marvin
    	 *  arthur: marvin
    	 */
    	Map<String,List<Movie>> movieMap = new HashMap<String,List<Movie>>();
        Movie foo = new Movie("Foo", 1993);
        Movie bar = new Movie("Bar", 1990);
        Movie marvin = new Movie("Marvin", 2011);
        
        List<Movie> baconList = new LinkedList<Movie>();
        List<Movie> spamList = new LinkedList<Movie>();
        List<Movie> arthurList = new LinkedList<Movie>();
        
        baconList.add(foo);
        baconList.add(bar);
        spamList.add(bar);
        arthurList.add(marvin);
        spamList.add(marvin);
        
        movieMap.put("Bacon, Kevin", baconList);
        movieMap.put("Spam, Kevin", spamList);
        movieMap.put("Dent, Arthur", arthurList);
        
        KevinBaconGameSolver solver = 
            KevinBaconGameSolver.createUnweightedGameSolver("Bacon, Kevin", movieMap);
        
        assertDoubleEquals("3 nodes unweighted", 
                solver.computeDistance("Dent, Arthur"), 2.0);
        
        KevinBaconGameSolver solver2 = 
            KevinBaconGameSolver.createWeightedGameSolver("Bacon, Kevin", movieMap);
        
        assertDoubleEquals("3 nodes weighted", 
                solver2.computeDistance("Dent, Arthur"), 21.0);
    }
	
}
